// CryMemoryManager.cpp : Defines the entry point for the DLL application.

#include "StdAfx.h"
#include "BitFiddling.h"

#include <stdio.h>
#include <ISystem.h>
#include <platform.h>

#include <CryMemoryAllocator.h>
#ifdef WIN32
#include "DebugCallStack.h"
#endif

#include "MemReplay.h"
#include "LevelHeap.h"
#include "MemoryManager.h"





volatile bool g_replayCleanedUp = false;





const bool bProfileMemManager = 0;

static CLevelHeap s_levelHeap;

#if !defined(PS3) && USE_LEVEL_HEAP
static volatile long s_levelHeapActive;
static size_t s_levelHeapViolationNumAllocs;
static size_t s_levelHeapViolationAllocSize;
static unsigned long s_levelHeapLocalTLS = TlsAlloc();

#if defined(USE_GLOBAL_BUCKET_ALLOCATOR)
extern void BucketAllocatorReplayRegisterAddressRange( const char *name );
#endif //defined(USE_GLOBAL_BUCKET_ALLOCATOR)

enum
{
	ActiveHeap_Default = 0,
	ActiveHeap_Global,
	ActiveHeap_Level,
};

static bool IsLevelHeapActive()
{
	uint32 threadLocalHeap = reinterpret_cast<uint32>(TlsGetValue(s_levelHeapLocalTLS));
	return (threadLocalHeap == ActiveHeap_Default && s_levelHeapActive) || (threadLocalHeap == ActiveHeap_Level);
}

void CCryMemoryManager::InitialiseLevelHeap()
{
	if (CLevelHeap::CanBeUsed())
	{
		s_levelHeap.Initialise();

#if CAPTURE_REPLAY_LOG
		s_levelHeap.ReplayRegisterAddressRanges();
#endif
	}

#if CAPTURE_REPLAY_LOG && defined(USE_GLOBAL_BUCKET_ALLOCATOR)
	BucketAllocatorReplayRegisterAddressRange( "Global Buckets" );
#endif
}

void CCryMemoryManager::SwitchToLevelHeap()
{
	if (s_levelHeap.IsValid())
	{
		s_levelHeapActive = 1;

		CryCleanup();
	}
}

void CCryMemoryManager::SwitchToGlobalHeap()
{
	if (s_levelHeap.IsValid())
	{
		s_levelHeapActive = 0;
		s_levelHeap.Cleanup();

#if TRACK_LEVEL_HEAP_USAGE
		s_levelHeap.GetState(s_levelHeapViolationNumAllocs, s_levelHeapViolationAllocSize);
#endif
	}
}

int CCryMemoryManager::LocalSwitchToGlobalHeap()
{
	int old = reinterpret_cast<int>(TlsGetValue(s_levelHeapLocalTLS));
	TlsSetValue(s_levelHeapLocalTLS, reinterpret_cast<void*>(ActiveHeap_Global));
	return old;
}

int CCryMemoryManager::LocalSwitchToLevelHeap()
{
	int old = reinterpret_cast<int>(TlsGetValue(s_levelHeapLocalTLS));
	TlsSetValue(s_levelHeapLocalTLS, reinterpret_cast<void*>(ActiveHeap_Level));
	return old;
}

void CCryMemoryManager::LocalSwitchToHeap(int heap)
{
	TlsSetValue(s_levelHeapLocalTLS, reinterpret_cast<void*>(heap));
}

bool CCryMemoryManager::GetLevelHeapViolationState(bool& usingLevelHeap, size_t& numAllocs, size_t& allocSize)
{
	usingLevelHeap = s_levelHeap.IsValid();

#if TRACK_LEVEL_HEAP_USAGE
	numAllocs = s_levelHeapViolationNumAllocs;
	allocSize = s_levelHeapViolationAllocSize;
#else
	numAllocs = 0;
	allocSize = 0;
#endif

	return numAllocs > 0;
}

#endif //!defined(PS3) && USE_LEVEL_HEAP

#if !USE_LEVEL_HEAP
void CCryMemoryManager::InitialiseLevelHeap() {}
void CCryMemoryManager::SwitchToLevelHeap() {}
void CCryMemoryManager::SwitchToGlobalHeap() {}
int CCryMemoryManager::LocalSwitchToGlobalHeap() { return 0; }
int CCryMemoryManager::LocalSwitchToLevelHeap() { return 0; }
void CCryMemoryManager::LocalSwitchToHeap(int heap) {}
bool CCryMemoryManager::GetLevelHeapViolationState(bool& usingLevelHeap, size_t& numAllocs, size_t& allocSize)
{
	usingLevelHeap = false;
	numAllocs = 0;
	allocSize = 0;
	return false;
}

#endif

#if defined(PS3) || !USE_LEVEL_HEAP
static bool IsLevelHeapActive() { return false; }
#endif


//////////////////////////////////////////////////////////////////////////
// Some globals for fast profiling.
//////////////////////////////////////////////////////////////////////////
long g_TotalAllocatedMemory = 0;

#ifndef CRYMEMORYMANAGER_API
#define CRYMEMORYMANAGER_API
#endif CRYMEMORYMANAGER_API

//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API size_t CryFree(void *p, size_t alignment);
CRYMEMORYMANAGER_API void CryFreeSize(void *p, size_t size);

CRYMEMORYMANAGER_API void CryCleanup();

// Undefine malloc for memory manager itself..
#undef malloc
#undef realloc
#undef free


//malloc and free resides in std on PS3



	#define _MSTD 



#define VIRTUAL_ALLOC_SIZE 524288

#include "CryMemoryAllocator.h"

#if !defined(PS3_DONT_USE_NODEALLOC)
	#ifdef USE_GLOBAL_BUCKET_ALLOCATOR
		#	include <BucketAllocatorImpl.h>



		BucketAllocator<BucketAllocatorDetail::DefaultTraits<BUCKET_ALLOCATOR_DEFAULT_SIZE, BucketAllocatorDetail::SyncPolicyLocked, true, 8> > g_GlobPageBucketAllocator;

	#else
		node_alloc<eCryMallocCryFreeCRTCleanup, true, VIRTUAL_ALLOC_SIZE> g_GlobPageBucketAllocator;
	#endif






































#endif ////!defined(PS3_DONT_USE_NODEALLOC)


//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API void *CryMalloc(size_t size, size_t& allocated, size_t alignment )
{
  if (!size)
	{
    allocated = 0;
		return 0;
  }

	MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);

	//FUNCTION_PROFILER_FAST( GetISystem(),PROFILE_SYSTEM,(bProfileMemManager && g_bProfilerEnabled) );

	uint8 *p;
	size_t sizePlus = size;

	if (IsLevelHeapActive())
	{
		if (alignment)
			p = (uint8*) s_levelHeap.Memalign(alignment,sizePlus);
		else
			p = (uint8*) s_levelHeap.Malloc(sizePlus);
	}
	else
	{	
		if (!alignment || g_GlobPageBucketAllocator.CanGuaranteeAlignment(sizePlus,alignment))
		{
			if (alignment)
				p = (uint8*)g_GlobPageBucketAllocator.allocate(sizePlus,alignment);
			else
				p = (uint8*)g_GlobPageBucketAllocator.alloc(sizePlus);
		}
		else
		{
			// emulate alignment
			sizePlus += alignment;			
#ifndef CAFE
      p = (uint8*) malloc(sizePlus);


#endif

			if (alignment && p)
			{
				uint8 offset = (BYTE)(alignment - ((UINT_PTR)p & (alignment - 1)));
				p += offset;
				p[-1] = offset;
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
#if !defined(USE_GLOBAL_BUCKET_ALLOCATOR)
	if (sizePlus < __MAX_BYTES + 1)
	{
		sizePlus = ((sizePlus - size_t(1)) >> (int)_ALIGN_SHIFT);
		sizePlus = (sizePlus + 1) << _ALIGN_SHIFT;
	}
#endif //!defined(USE_GLOBAL_BUCKET_ALLOCATOR)

	if (!p)
	{
		allocated = 0;
		gEnv->bIsOutOfMemory = true;
		CryFatalError( "*** Memory allocation for %u bytes failed ****",(unsigned int)sizePlus );
		return 0;		// don't crash - allow caller to react
	}

	CryInterlockedExchangeAdd(&g_TotalAllocatedMemory, sizePlus);
	allocated = sizePlus;

	MEMREPLAY_SCOPE_ALLOC(p, sizePlus, 0);

	assert(alignment == 0 || (reinterpret_cast<UINT_PTR>(p) & (alignment-1)) == 0);
	return p;
}

//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API size_t CryGetMemSize(void *memblock, size_t sourceSize)
{
	//	ReadLock lock(g_lockMemMan);
#if !defined(PS3_DONT_USE_NODEALLOC)
#if !defined(PS3) && USE_LEVEL_HEAP
		if (s_levelHeap.IsInAddressRange(memblock))
			return s_levelHeap.UsableSize(memblock);
		else
#endif
			return g_GlobPageBucketAllocator.getSize(memblock);


#endif
}

//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API void *CryRealloc(void *memblock, size_t size, size_t& allocated,size_t& oldsize, size_t alignment )
{
	//FUNCTION_PROFILER_FAST( GetISystem(),PROFILE_SYSTEM,(bProfileMemManager && g_bProfilerEnabled) );
	if (memblock==NULL)
	{
		oldsize = 0;
		return CryMalloc(size, allocated, alignment);
	}
	else
	{
		MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CrtMalloc);
		
		void *np;
		np = CryMalloc(size, allocated,alignment);

		// get old size		
		if (s_levelHeap.IsInAddressRange(memblock))
		{
			oldsize = s_levelHeap.UsableSize(memblock);  
		}
		else
		{
			if (g_GlobPageBucketAllocator.IsInAddressRange(memblock))
			{
				oldsize = g_GlobPageBucketAllocator.getSize(memblock);
			}
			else
			{
				uint8* pb = static_cast<uint8*>(memblock); 
				int adj = 0;
				if(alignment)		
					adj = pb[-1];
				oldsize = CrySystemCrtSize(pb - adj) - adj;
			}
		}

		if (!np && size)
		{
			gEnv->bIsOutOfMemory = true;
			CryFatalError( "*** Memory allocation for %u bytes failed ****",(unsigned int)size );
			return 0;		// don't crash - allow caller to react
		}

		// copy data over
		memcpy(np, memblock, size>oldsize ? oldsize : size);
		CryFree(memblock,alignment);

		MEMREPLAY_SCOPE_REALLOC(memblock, np, size, alignment);

		assert( alignment == 0 || (reinterpret_cast<UINT_PTR>(np) & (alignment-1)) == 0);
		return np;
	}
}

//////////////////////////////////////////////////////////////////////////
size_t CryFree(void *p,size_t alignment) 
{
	size_t size = 0;
	//FUNCTION_PROFILER_FAST( GetISystem(),PROFILE_SYSTEM,(bProfileMemManager && g_bProfilerEnabled) );

	UINT_PTR pid = (UINT_PTR)p;

	if (p != NULL)
	{
		MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);

		if ( s_levelHeap.IsInAddressRange(p) )
		{
			size = s_levelHeap.UsableSize(p);
			s_levelHeap.Free(p);
		}
		else
		{
			if (g_GlobPageBucketAllocator.IsInAddressRange(p))
			{
				size = g_GlobPageBucketAllocator.deallocate(p);				
			}
			else
			{
				if (alignment)
				{				
					uint8* pb = static_cast<uint8*>(p);					
					pb -= pb[-1];
#ifndef CAFE
					size = CrySystemCrtSize(pb);
          free(pb);


#endif
				}
				else
				{
#ifndef CAFE
          size = CrySystemCrtSize(p);
          free(p);


#endif
				}
			}
		}
		
		long lsize = size;
		CryInterlockedExchangeAdd(&g_TotalAllocatedMemory, -lsize);

		MEMREPLAY_SCOPE_FREE(pid);
	}

	return size;
}

CRYMEMORYMANAGER_API void CryFlushAll()  // releases/resets ALL memory... this is useful for restarting the game
{
	g_TotalAllocatedMemory = 0;
};

//////////////////////////////////////////////////////////////////////////
// Returns amount of memory allocated with CryMalloc/CryFree functions.
//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API int CryMemoryGetAllocatedSize()
{
	return g_TotalAllocatedMemory;
}

//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API int CryMemoryGetPoolSize()
{
	return 0;
}

//////////////////////////////////////////////////////////////////////////
CRYMEMORYMANAGER_API int CryStats(char *buf)
{
	return 0;
}

CRYMEMORYMANAGER_API int CryGetUsedHeapSize()
{



	return g_GlobPageBucketAllocator.get_heap_size();

}

CRYMEMORYMANAGER_API int CryGetWastedHeapSize()
{



	return g_GlobPageBucketAllocator.get_wasted_in_allocation();

}





CRYMEMORYMANAGER_API void CryCleanup()
{
#if !defined(PS3_DONT_USE_NODEALLOC)
	g_GlobPageBucketAllocator.cleanup();
#elif defined(USE_GLOBAL_BUCKET_ALLOCATOR)
	CleanupGlobalBucketAllocator();
#endif
}

#if defined(USE_GLOBAL_BUCKET_ALLOCATOR) && !defined(PS3_DONT_USE_NODEALLOC)
void EnableDynamicBucketCleanups(bool enable)
{
	g_GlobPageBucketAllocator.EnableExpandCleanups(enable);
#if USE_LEVEL_HEAP
	s_levelHeap.EnableExpandCleanups(enable);
#endif
}
void BucketAllocatorReplayRegisterAddressRange( const char *name )
{
#if CAPTURE_REPLAY_LOG
	g_GlobPageBucketAllocator.ReplayRegisterAddressRange( name );
#endif //CAPTURE_REPLAY_LOG
}
#endif //defined(USE_GLOBAL_BUCKET_ALLOCATOR) && !defined(PS3_DONT_USE_NODEALLOC)








CRYMEMORYMANAGER_API void *CrySystemCrtMalloc(size_t size)
{
	void* ret=NULL;
	ret=_MSTD::malloc(size);
	return ret;
}

CRYMEMORYMANAGER_API void *CrySystemCrtRealloc(void * p, size_t size)
{
	void *ret;
	ret=_MSTD::realloc(p, size);
	return ret;
}

CRYMEMORYMANAGER_API size_t CrySystemCrtFree(void *p)
{
	size_t n = 0;
#if defined(WIN32) || defined(WIN64) || defined (XENON) || defined(PS3) || defined(gringo) || defined(CAFE)
	n = /*_MSTD::*/_msize(p);
#else
	n = malloc_usable_size(p);
#endif // WIN32 || WIN64 || XENON || PS3 || gringo || CAFE
	_MSTD::free(p);
	return n;
}

CRYMEMORYMANAGER_API size_t CrySystemCrtSize(void *p)
{
#if defined(WIN32) || defined(WIN64) || defined (XENON) || defined(PS3) || defined(gringo)  || defined(CAFE)
	return _msize(p);
#else
	return malloc_usable_size(p);
#endif
}

CRYMEMORYMANAGER_API void CryResetStats(void)
{
}

int GetPageBucketAlloc_wasted_in_allocation()
{



	return g_GlobPageBucketAllocator.get_wasted_in_allocation();

}

int GetPageBucketAlloc_get_free()
{



	return g_GlobPageBucketAllocator._S_get_free();

}

